
/**********************************************************************************\
** 文件名称: oled.c
** 创建人员: YCG
** 创建日期: 2018-12-25
** 文档描述:
** 版本代号: V0.1
** 版本说明: 初始版本
\********************************************************************************/

#include "oled.h"
#include "IIC.h"
#include "font.h"
#include "print.h"

static uint8_t OledBuf[OLED_BUF_SIZE] = {0x40};
static uint8_t OledCmd[2] = {0x80};
static uint8_t* OleDatadBuf = OledBuf + 1;
/*******************************************************************************
** 函数名称: OLED_Init
** 功能描述: OLED初始化
** 参数说明: None
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_Init(void)
{
    IIC_Init();
    SET_RES_HIGH;
    IIC_DelayUs(10);
    SET_RES_LOW;
    IIC_DelayUs(10);
    SET_RES_HIGH;
    IIC_DelayUs(10);

    OLED_SendCmd(0xae); //关闭显示

    OLED_SendCmd(0xd5); //设置显示时钟分频/振荡器频率
    OLED_SendCmd(0xc4); //100HZ

    OLED_SendCmd(0xd9); //设置预充电周期
    OLED_SendCmd(0x22); //

    OLED_SendCmd(0x20); //设置存储器寻址方式
    OLED_SendCmd(0x00); //水平

    OLED_SendCmd(0xa0); //seg re-map 0->127
    OLED_SendCmd(0xc8); //垂直翻转 C0 C8

    OLED_SendCmd(0xda); //设置COM引脚硬件配置
    OLED_SendCmd(0x12);

    OLED_SendCmd(0x81); //设置对比度
    OLED_SendCmd(0xFF);

    OLED_SendCmd(0xb0); //页寻址模式设置页开始地址

    //OLED_SendCmd(0x40);

    OLED_SendCmd(0xd3); //设置显示偏移量
    OLED_SendCmd(0x1f); //

    OLED_SendCmd(0xa6); //正常显示，A7反相显示

    OLED_SendCmd(0xa8); //设置多路复用率
    OLED_SendCmd(0x0f); //设置为16路,16行

    OLED_SendCmd(0xa4); //全局显示开启

    OLED_SendCmd(0xdb); //设置VCOMH输出级别
    OLED_SendCmd(0x30); //0.83*VCC

    OLED_SendCmd(0x21);//设置列
    OLED_SendCmd(0x20);//起始地址
    OLED_SendCmd(0xFF);//结束地址

    OLED_SendCmd(0x22);//设置页
    OLED_SendCmd(0x00);//起始地址
    OLED_SendCmd(0x01);//结束地址


    OLED_SendCmd(0xaf); //开启显示
    OLED_Clear();
    OLED_SetPos(0, 0);
}

/*******************************************************************************
** 函数名称: OLED_SendCmd
** 功能描述: OLED发送指令
** 参数说明: cmd: [输入]  指令
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_SendCmd(uint8_t cmd)
{
#ifdef I2C_USE_HARDWARE

    while(I2C_IS_BUSY) {}  //等待DMA传输完成

    OledCmd[1] = cmd;

    I2C_SetStart(2, OledCmd);
#else
    IIC_Start();
    IIC_SendByte(0x78);
    IIC_SendByte(0x80);
    IIC_SendByte(cmd);
    IIC_Stop();
#endif
}

void OLED_Update(void) {

    while(I2C_IS_BUSY) {} //等待IIC总线有空
    I2C_SetStart(sizeof(OledBuf), OledBuf);
}

void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) {
    const uint8_t
    SetBit[] = {   0x01, 0x02, 0x04, 0x08, 0x10,  0x20, 0x40, 0x80 },
               ClrBit[] = {  0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F  };

    if((x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) return;
    if(color) OleDatadBuf[x + (y >> 3) * OLED_WIDTH] |= SetBit[y & 7];
    else      OleDatadBuf[x + (y >> 3) * OLED_WIDTH] &= ClrBit[y & 7];
}

void OLED_DrawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
    int16_t steep = abs(y1 - y0) > abs(x1 - x0);
    if (steep) {
        SWAP(x0, y0);
        SWAP(x1, y1);
    }
    if (x0 > x1) {
        SWAP(x0, x1);
        SWAP(y0, y1);
    }
    int16_t dx, dy;
    dx = x1 - x0;
    dy = abs(y1 - y0);

    int16_t err = dx / 2;
    int16_t ystep;

    if (y0 < y1) {
        ystep = 1;
    } else {
        ystep = -1;
    }
    for (; x0 <= x1; x0++) {
        if (steep) {
            OLED_DrawPixel(y0, x0, color);
        } else {
            OLED_DrawPixel(x0, y0, color);
        }
        err -= dy;
        if (err < 0) {
            y0 += ystep;
            err += dx;
        }
    }
}

void OLED_DrawVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
    OLED_DrawLine(x, y, x, y + h - 1, color);
}

void OLED_DrawHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
    OLED_DrawLine(x, y, x + w - 1, y, color);
}

void OLED_DrawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
    int16_t f = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;

    OLED_DrawPixel(x0, y0 + r, color);
    OLED_DrawPixel(x0, y0 - r, color);
    OLED_DrawPixel(x0 + r, y0, color);
    OLED_DrawPixel(x0 - r, y0, color);

    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        OLED_DrawPixel(x0 + x, y0 + y, color);
        OLED_DrawPixel(x0 - x, y0 + y, color);
        OLED_DrawPixel(x0 + x, y0 - y, color);
        OLED_DrawPixel(x0 - x, y0 - y, color);
        OLED_DrawPixel(x0 + y, y0 + x, color);
        OLED_DrawPixel(x0 - y, y0 + x, color);
        OLED_DrawPixel(x0 + y, y0 - x, color);
        OLED_DrawPixel(x0 - y, y0 - x, color);
    }
}

void OLED_DrawCircleHelper( int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color) {
    int16_t f     = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x     = 0;
    int16_t y     = r;

    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;
        if (cornername & 0x4) {
            OLED_DrawPixel(x0 + x, y0 + y, color);
            OLED_DrawPixel(x0 + y, y0 + x, color);
        }
        if (cornername & 0x2) {
            OLED_DrawPixel(x0 + x, y0 - y, color);
            OLED_DrawPixel(x0 + y, y0 - x, color);
        }
        if (cornername & 0x8) {
            OLED_DrawPixel(x0 - y, y0 + x, color);
            OLED_DrawPixel(x0 - x, y0 + y, color);
        }
        if (cornername & 0x1) {
            OLED_DrawPixel(x0 - y, y0 - x, color);
            OLED_DrawPixel(x0 - x, y0 - y, color);
        }
    }
}

void OLED_FillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color) {

    int16_t f     = 1 - r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x     = 0;
    int16_t y     = r;

    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;

        if (cornername & 0x1) {
            OLED_DrawVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
            OLED_DrawVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
        }
        if (cornername & 0x2) {
            OLED_DrawVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
            OLED_DrawVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
        }
    }
}

void OLED_FillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) {
    OLED_DrawVLine(x0, y0 - r, 2 * r + 1, color);
    OLED_FillCircleHelper(x0, y0, r, 3, 0, color);
}
void OLED_DrawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
    OLED_DrawHLine(x, y, w, color);
    OLED_DrawHLine(x, y + h - 1, w, color);
    OLED_DrawVLine(x, y, h, color);
    OLED_DrawVLine(x + w - 1, y, h, color);
}

void OLED_DrawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
    // smarter version

    OLED_DrawHLine(x + r, y, w - 2 * r, color); // Top
    OLED_DrawHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
    OLED_DrawVLine(x, y + r, h - 2 * r, color); // Left
    OLED_DrawVLine(x + w - 1, y + r, h - 2 * r, color); // Right
    // draw four corners
    OLED_DrawCircleHelper(x + r, y + r, r, 1, color);
    OLED_DrawCircleHelper(x + w - r - 1, y + r, r, 2, color);
    OLED_DrawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
    OLED_DrawCircleHelper(x + r, y + h - r - 1, r, 8, color);

}
void OLED_FillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) {
    // smarter version
    OLED_FillRect(x + r, y, w - 2 * r, h, color);
    // draw four corners
    OLED_FillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
    OLED_FillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
}

void OLED_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
    for (int16_t i = x; i < x + w; i++) {
        for (int16_t j = y; j < y + h; j++) {
            OLED_DrawPixel(i, j, color);
        }
    }

}

void OLED_DrawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) {
    OLED_DrawLine(x0, y0, x1, y1, color);
    OLED_DrawLine(x1, y1, x2, y2, color);
    OLED_DrawLine(x2, y2, x0, y0, color);
}

void OLED_FillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) {

    int16_t a, b, y, last;

    // Sort coordinates by Y order (y2 >= y1 >= y0)
    if (y0 > y1) {
        SWAP(y0, y1);
        SWAP(x0, x1);
    }
    if (y1 > y2) {
        SWAP(y2, y1);
        SWAP(x2, x1);
    }
    if (y0 > y1) {
        SWAP(y0, y1);
        SWAP(x0, x1);
    }

    if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
        a = b = x0;
        if(x1 < a)      a = x1;
        else if(x1 > b) b = x1;
        if(x2 < a)      a = x2;
        else if(x2 > b) b = x2;
        OLED_DrawHLine(a, y0, b - a + 1, color);
        return;
    }

    int16_t
    dx01 = x1 - x0,
    dy01 = y1 - y0,
    dx02 = x2 - x0,
    dy02 = y2 - y0,
    dx12 = x2 - x1,
    dy12 = y2 - y1;
    int32_t
    sa   = 0,
    sb   = 0;

    // For upper part of triangle, find scanline crossings for segments
    // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
    // is included here (and second loop will be skipped, avoiding a /0
    // error there), otherwise scanline y1 is skipped here and handled
    // in the second loop...which also avoids a /0 error here if y0=y1
    // (flat-topped triangle).
    if(y1 == y2) last = y1;   // Include y1 scanline
    else         last = y1 - 1; // Skip it

    for(y = y0; y <= last; y++) {
        a   = x0 + sa / dy01;
        b   = x0 + sb / dy02;
        sa += dx01;
        sb += dx02;
        /* longhand:
        a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) SWAP(a, b);
        OLED_DrawHLine(a, y, b - a + 1, color);
    }

    // For lower part of triangle, find scanline crossings for segments
    // 0-2 and 1-2.  This loop is skipped if y1=y2.
    sa = dx12 * (y - y1);
    sb = dx02 * (y - y0);
    for(; y <= y2; y++) {
        a   = x1 + sa / dy12;
        b   = x0 + sb / dy02;
        sa += dx12;
        sb += dx02;
        /* longhand:
        a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) SWAP(a, b);
        OLED_DrawHLine(a, y, b - a + 1, color);
    }
}

void OLED_DrawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color) {

    uint8_t byte = 0;
    for(int16_t i = 0; i < w; i++, x++) {
        for(int16_t j = 0; j < h; j++) {
            if(j & 7) byte >>= 1;
            else      byte   = bitmap[(j / 8) * OLED_WIDTH + i];
            if(byte & 0x01) OLED_DrawPixel(x, y + j, color);
        }
    }
}

void OLED_DrawChar(int16_t x, int16_t y, uint8_t c,  uint16_t color) {
    if(color) OLED_FillRect(x, y, 6, 8, 0);
    else OLED_FillRect(x, y, 6, 8, 1);
    OLED_DrawBitmap(x, y, F6x8[c - ' '], 6, 8, color);
}

uint8_t Print_X = 0, Print_Y = 0;
void OLED_SendChar(uint8_t c) {
    if(c == '\n') {
        Print_X = 0;
        Print_Y = (Print_Y + 8) % OLED_HEIGHT; //换行
    } else {
        OLED_DrawChar(Print_X, Print_Y, c, 1);
        Print_X += 6;
        if(Print_X > (OLED_WIDTH - 6)) {
            Print_X = 0;
            Print_Y = (Print_Y + 8) % OLED_HEIGHT; //换行
        }
    }
}

void OLED_Printf(int16_t x, int16_t y, char *fmt, ...)
{
    va_list arp;
    va_start (arp, fmt); // 将可变长参数转换为va_list
    Print_X = x;
    Print_Y = y;
    Print(OLED_SendChar, fmt, arp);
    va_end (arp);
}

void OLED_UPrintf(int16_t x, int16_t y, char *fmt, ...)
{
    va_list arp;
    va_start (arp, fmt); // 将可变长参数转换为va_list
    Print_X = x;
    Print_Y = y;
    Print(OLED_SendChar, fmt, arp);
    OLED_Update();
    va_end (arp);
}

/*******************************************************************************
** 函数名称: OLED_SetPos
** 功能描述: OLED设置显示位置
** 参数说明: x: [输入/出]
**			 y: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_SetPos(uint8_t x, uint8_t y)
{
    OLED_SendCmd(0xb0 + y);
    OLED_SendCmd(x % 15);
    OLED_SendCmd(0x12 + x / 15);
}

/*******************************************************************************
** 函数名称: OLED_Char
** 功能描述: OLED显示字符
** 参数说明: x: [输入/出]
**			 y: [输入/出]
**			 ch: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_Char(uint8_t x, uint8_t y, char ch)
{
#ifdef I2C_USE_HARDWARE


#else
    uint8_t i;
    if(OLED_SetPos(x * 6, y))
    {
        ch = ch - ' ';
        IIC_Start();
        IIC_SendByte(0x78);
        IIC_SendByte(0x40);
        for(i = 0; i < 6; i++)
        {
            IIC_SendByte( F6x8[ch][i]);
        }
        IIC_Stop();
    }
#endif
}


/*******************************************************************************
** 函数名称: OLED_String
** 功能描述: OLED显示字符串
** 参数说明: x: [输入/出]
**			 y: [输入/出]
**			 st: [输入/出]
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_String(uint8_t x, uint8_t y, char* st)
{

#ifdef I2C_USE_HARDWARE

//    while(I2C_IS_BUSY) {}   //等待IIC总线有空

//    while(*st != '\0')
//    {
//        for(i = 0; i < 6; i++)
//        {
//            OledBuf[j++] = F6x8[*st - ' '][i];
//        }
//        st++;
//    }
//    I2C_SetStart(j, OledBuf);
#else
    uint8_t i, j = 1;
    OLED_SetPos(x * 6, y);
    IIC_Start();
    IIC_SendByte(0x78);
    IIC_SendByte(0x40);
    while(*st != '\0')
    {
        for(i = 0; i < 6; i++)
        {
            IIC_SendByte( F6x8[*st - ' '][i]);
        }
        st++;
    }
    IIC_Stop();

#endif
}


/*******************************************************************************
** 函数名称: OLED_Image
** 功能描述: OLED显示图片
** 参数说明: None
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_Image(void)
{
#ifdef I2C_USE_HARDWARE
#else
    uint8_t i, k;
    for(k = 0; k < 2; k++)
    {
        OLED_SendCmd(0xb0 + k);
        OLED_SendCmd(0x12);
        OLED_SendCmd(0x00);
        {
            IIC_Start();
            IIC_SendByte(0x78);
            IIC_SendByte(0x40);
            for(i = 0; i < 96; i++)
            {
                IIC_SendByte(logo2[96 * k + i]);
            }
            IIC_Stop();
            IIC_DelayUs(10);
        }
    }
#endif
}

/*******************************************************************************
** 函数名称: OLED_Clear
** 功能描述: OLED清屏
** 参数说明:
** 返回说明: None
** 创建人员: YCG
** 创建日期: 2018-12-25
********************************************************************************/
void OLED_Clear(void)
{
#ifdef I2C_USE_HARDWARE
    for(uint8_t i = 0; i < (OLED_BUF_SIZE - 1); i++)
        OleDatadBuf[i] = 0 ;
#else
    uint8_t i, k;
    for(k = 0; k < 2; k++)
    {
        OLED_SendCmd(0xb0 + k);
        OLED_SendCmd(0x12);
        OLED_SendCmd(0x00);
        {
            IIC_Start();
            IIC_SendByte(0x78);
            IIC_SendByte(0x40);
            for(i = 0; i < 96; i++)
            {
                IIC_SendByte(0);
            }
            IIC_Stop();
            IIC_DelayUs(1);
        }
    }
#endif
}




/********************************End of File************************************/

